{-
    Main.hs: Main module for the Die Roll Analysis program.
    ----------------------------------------------------------------------------
    Version: 1.0alpha1
    Author:  Christopher Granade (cgranade@gmail.com)
    ----------------------------------------------------------------------------
    This file is a part of the Die Rolling Analysis Toolkit (DRAT) project.
    http://drat.googlecode.com/
    ----------------------------------------------------------------------------
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 2 or 3 of the License.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    ----------------------------------------------------------------------------
    (c) 2007 Christopher E. Granade
-}
module Main where
    -- Import other project modules.
    import Roll
    import Tuple3
    
    -- Import needed Prelude sections.
    import Monad
    import IO
    
    -- Import argument handling functions.
    import System (getArgs)
    import System.Console.GetOpt
        
    -- Import string formatting library.
    import Text.Printf
        
    -- Declare used data types.
    data Flag = Version | Help | NumDice Int | DieSize Int | BestNum Int | SetAction Action
    data State = State Int Int Int Action -- DieSize NumDice TakeBest Action
    data Action = Describe | Hist

    -- Declare constant functions
    header :: String
    header =  "Usage: dierolling [OPTIONS...]"
    
    nop :: IO ()
    nop =  putStr ""
    
    trials :: Int
    trials =  50000
    
    options :: [OptDescr Flag]
    options = [ Option ['V'] ["version"] (NoArg Version) "show version number",
                Option ['?'] ["help"] (NoArg Help) "shows help message",
                Option ['n'] ["numdice"] (ReqArg (\s -> NumDice (read s)) "NUM") "sets the number of dice to roll",
                Option ['b'] ["bestnum"] (ReqArg (\s -> BestNum (read s)) "BEST") "sets the best dice to take",
                Option ['s'] ["diesize"] (ReqArg (\s -> DieSize (read s)) "SIZE") "sets the size of the rolled dice",
                Option ['d'] ["describe"] (NoArg (SetAction Describe)) "calculates the mean, standard deviation and skew of a sample rolling",
                Option ['h'] ["hist"] (NoArg (SetAction Hist)) "calculates the histogram of scores from a sample rolling"]

    -- Begin main function
    main :: IO ()
    main =  do
        args <- getArgs
        case getOpt Permute options args of
            (o,n,[]  ) -> let optsRes = (handleOpts	 (State 6 4 2 Describe) o) in do
                sequence $ fst optsRes
                printState (snd optsRes)
                actionResult <- performAction (snd optsRes)
                putStrLn $ actionResult
            (_,_,errs) -> ioError (userError (concat errs ++ usageInfo header options))

    -- Functions to generate the actual results.
    performAction :: State -> IO String
    performAction (State s n b Describe) =
        let desc = (liftM $ describeRolls $ sumBest b) (monteCarlo n s trials) in do
            -- printState (State s n b Describe)
            liftM showDescript desc
    performAction (State s n b Hist) = do
        liftM show (liftM (histOfRolls $ sumBest b) (monteCarlo n s trials))

    -- Declare text processing functions.
    showRollNotation       :: Int -> Int -> Int -> String
    showRollNotation n s b =  (show n ++ "d" ++ show s ++ "b" ++ show b)
    
    showDescript ((q1, q2, q3), mn, sd, sk) = printf
        ("\tQuartiles:\n" ++ "\t\t%.3f\t%.3f\t%.3f\n" ++
        "\tMean = %.3f\tStd dev = %.3f\tSkewness = %.5f")
        q1 q2 q3 (mn :: Double) sd (sk :: Double)
    
    printState :: State -> IO ()
    printState (State s n b Describe) =
    	putStrLn ("Describing " ++ showRollNotation n s b ++ ", using " ++ show trials ++ " trials:")
    printState (State s n b Hist) =
    	putStrLn ("Histogram of " ++ showRollNotation n s b ++ ", using " ++ show trials ++ " trials:")
        
    -- Option handling code    
    handleOpts          :: State -> [Flag] -> ([IO ()], State)
    handleOpts s []     = ([], s)
    handleOpts s (o:os) = let thisRes = (handleOpt s o)
                              otherRes = (handleOpts (snd thisRes) os) in
        (((fst thisRes):fst otherRes), snd otherRes)
        
    handleOpt                            :: State -> Flag -> (IO (), State)
    handleOpt (State s n b a) (NumDice n') =  (nop, State s n' b a)
    handleOpt (State s n b a) (DieSize s') =  (nop, State s' n b a)
    handleOpt (State s n b a) (BestNum b') =  (nop, State s n b' a)
    handleOpt (State s n b _) (SetAction a) = (nop, State s n b a)
    handleOpt s (Help)                   =  (putStrLn (usageInfo header options), s)
   